#region The MIT License
// The MIT License
// 
// Copyright (c) 2009 octalforty studios
// 
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#endregion
using System;
using System.ComponentModel.Composition;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Net.Mime;

using octalforty.Componento.ComponentModel.Caching;
using octalforty.Componento.ComponentModel.Installation.Web;
using octalforty.Componento.ComponentModel.Tracing;
using octalforty.Componento.Shared.SemanticModel;
using octalforty.Componento.Util;

namespace octalforty.Componento.ComponentModel.Installation
{
    [ResourceProvider()]
    public class HttpFileResourceProvider : IResourceProvider
    {
        #region Public Properties
        public ITraceManagerService TraceManager
        { get; set;  }

        public ITrace Trace
        { get; set; }
        /// <summary>
        /// Gets or sets a reference to the <see cref="IHttpRequestFactoryService"/>.
        /// </summary>
        [Import()]
        public IHttpRequestFactoryService HttpRequestFactory 
        { get; set; }

        [Import()]
        public ICacheManagerService CacheManager 
        { get; set; }
        #endregion

        [ImportingConstructor()]
        public HttpFileResourceProvider(ITraceManagerService traceManager)
        {
            TraceManager = traceManager;
            Trace = TraceManager.GetTrace(GetType().Name);
        }

        #region IResourceProvider Members
        /// <summary>
        /// Verifies whether current <see cref="IResourceProvider"/> can retrieve resources from <paramref name="uri"/>.
        /// </summary>
        /// <param name="uri"></param>
        /// <returns></returns>
        public virtual bool CanProvideResourcesFrom(Uri uri)
        {
            return uri.Scheme.ToLowerInvariant() == "http";
        }

        /// <summary>
        /// Retrieves resources from <paramref name="uri"/> for package <paramref name="packageName"/> with
        /// version requirement <paramref name="versionSpecification"/>.
        /// </summary>
        /// <param name="uri"></param>
        /// <param name="packageName"></param>
        /// <param name="versionSpecification"></param>
        /// <returns></returns>
        public virtual Uri[] GetResources(Uri uri, string packageName, VersionSpecification versionSpecification)
        {
            //
            // Checking cache
            var cacheEntryInfo = CacheManager.GetCacheEntryInfo(uri.ToString());

            var httpRequest = CreateHttpRequest(uri);
            if(cacheEntryInfo != null)
                httpRequest.IfModifiedSince = cacheEntryInfo.UpdatedOn;

            #region Tracing
            Trace.Verbose("Issuing HTTP GET request to '{0}'", uri);
            #endregion

            using(var httpResponse = httpRequest.GetResponse())
            {
                #region Tracing
                Trace.Verbose("HTTP response: {0} {1}", (int)httpResponse.StatusCode, httpResponse.StatusDescription);
                #endregion

                if(httpResponse.StatusCode == HttpStatusCode.OK)
                {
                    //
                    // Check if we have "Content-Disposition" present.
                    if(!string.IsNullOrEmpty(httpResponse.Headers["Content-Disposition"]))
                    {
                        var contentDisposition = new ContentDisposition(httpResponse.Headers["Content-Disposition"]);
                        
                        var uriBuilder = new UriBuilder(new Uri(UriUtil.GetUriWithoutQuery(uri), contentDisposition.FileName));
                        uriBuilder.Query = UriUtil.GetBareQuery(uri);

                        // TODO: Yes, this is bad
                        uri = uriBuilder.Uri;
                    } // if

                    using(Stream responseStream = httpResponse.GetResponseStream())
                    {
                        #region Tracing
                        Trace.Verbose("Downloading {0:N2} Kb", httpResponse.ContentLength / 1024.0);
                        Stopwatch stopwatch = Stopwatch.StartNew();
                        #endregion

                        CacheManagerServiceUtil.Add(CacheManager, uri.ToString(), responseStream);

                        #region Tracing
                        Trace.Verbose("Downloaded {0:N2} Kb in {1}", httpResponse.ContentLength, stopwatch.Elapsed);
                        #endregion
                    } // using
                } // if
                else if(httpResponse.StatusCode != HttpStatusCode.NotModified)
                {
                    throw ExceptionUtil.ComponentManifestException(null,
                                                                   "Cannot retrieve Component Manifest for Component '{0}' from URI '{1}': HTTP {2} {3}",
                                                                   packageName, uri, httpResponse.StatusCode, httpResponse.StatusDescription);
                }

                return new Uri[] { new Uri("cache+" + uri) };
            } // using
        }

        protected virtual IHttpRequest CreateHttpRequest(Uri uri)
        {
            return HttpRequestFactory.CreateHttpRequest(HttpMethod.Get, uri);
        }
        #endregion
    }
}